package name.wilbur.course.db.bigone.dao;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;

import name.wilbur.course.db.bigone.util.ConfigHelper;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.log4j.PropertyConfigurator;

public class DAOFactory {
	public static enum DBA_LEVEL { GOD, ADMIN, NORMAL };
	public static BasicDataSource globalDataSource;
	
	public static IDAO createDAO(Class<?> daoClass) {
		BaseDAO dao = null;
		try {
			dao = (BaseDAO) daoClass.newInstance();
			if (!(dao instanceof BaseDAO))
				throw new DAONotFoundException();
			dao.setDataSource(globalDataSource);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return dao;
	}
	
	// initialize connection pool first (once)
	public static void initialize(DBA_LEVEL level) {
		/* setup global datasource */
		globalDataSource = setupDataSource();
		authorize(level);
		/* setup log4j */
		PropertyConfigurator.configure(
				ConfigHelper.findAsResource("config/logging.properties")
		);
	}
	// change role of the DBA
	// must restart connection pool to ensure all connection's passwd and username
	// have been updated.
	public static void authorize(DBA_LEVEL level) {
		String levelStr = "normal";
		InputStream db_in = null;
		Properties props = new Properties();
		try {
			db_in = ConfigHelper.getUserResourceAsStream("config/database.properties");
			props.load(db_in);
			// default normal
			switch (level) {
			case GOD:
				levelStr = "god";
				break;
			case ADMIN:
				levelStr = "admin";
				break;
			}
			globalDataSource.setUsername(props.getProperty("user_" + levelStr));
			globalDataSource.setPassword(props.getProperty("passwd_" + levelStr));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (db_in != null) db_in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	private static BasicDataSource setupDataSource() {
		InputStream dbcp_in = null;
		Properties props = new Properties();
		BasicDataSource bds = null;
		try {
			dbcp_in = ConfigHelper.getUserResourceAsStream("config/dbcp.properties");
			bds = new BasicDataSource();
			props.load(dbcp_in);
			bds.setDriverClassName(props.getProperty("driver"));
			bds.setUrl(props.getProperty("protocol") + props.getProperty("location"));
			// connection pool setup
			bds.setInitialSize(Integer.parseInt(props.getProperty("init-size")));
			bds.setMaxActive(Integer.parseInt(props.getProperty("max-active")));
			bds.setMaxIdle(Integer.parseInt(props.getProperty("max-idle")));
			bds.setMaxWait(Long.parseLong(props.getProperty("max-wait")));

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (dbcp_in != null) dbcp_in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return bds;
	}
	
	public static void cleanup() {
		try {
			shutdownDataSource(globalDataSource);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	private static void shutdownDataSource(DataSource ds) throws SQLException {
		BasicDataSource bds = (BasicDataSource) ds;
		globalDataSource.close();
	}
	
	public void printDSStatus() {
		BasicDataSource bds = (BasicDataSource) globalDataSource;
		System.out.println("Init size: " + bds.getInitialSize());
		System.out.println("MaxActive: " + bds.getMaxActive());
		System.out.println("NumActive: " + bds.getNumActive());
		System.out.println("MaxIdle: " + bds.getMaxIdle());
		System.out.println("NumIdle: " + bds.getNumIdle());
	}
}
